#include "LocalPartition.h"


LocalPartition::LocalPartition(uint32_t size,uint32_t victimPolicy):_size(size),
                                _victimPolicy(victimPolicy),_count(0),
                                _victimIndex(0),_L0filter(0),_NRU(0),_NRUmodified(0)
{
    _pages = new uint32_t[_size];
    memset(_pages,0,sizeof(uint32_t)*_size);
    _NRU.reset();
    _NRUmodified.reset();
    _L0filter.reset();

    if (_victimPolicy==VICTIM_LRU)  {
        _LRU = new uint32_t[_size];
        memset(_LRU,0,sizeof(uint32_t)*_size);
    }
}

LocalPartition::~LocalPartition()
{
    delete _pages;
    if (_victimPolicy==VICTIM_LRU)
        delete _LRU;
}

void
LocalPartition::reset()
{
    memset(_pages,0,sizeof(uint32_t)*_size);
    _count=0;
    _victimIndex=0;
    _NRU.reset();
    _NRUmodified.reset();
    _L0filter.reset();

}

bool
LocalPartition::find(uint32_t page)
{
    for (int i=0;i<_size;i++) {
        if (_pages[i]==page)
            return true;
    }
    return false;
}

void
LocalPartition::updateVictimPolicy(uint32_t page,uint32_t index)
{
    if (_victimPolicy==VICTIM_LRU && index<_size) {
        if (index!=0) {
            // swap pointers
            uint32_t j=_LRU[index-1];
            _LRU[index-1]=_LRU[index];
            _LRU[index]=j;
        }
    }

}

    void
LocalPartition::read(uint32_t page,bool remote)
{
    uint32_t i=0;

    for (i=0;i<_size;i++) {
        if (_pages[i]==page) {
            if (_victimPolicy==VICTIM_NRU){
                _NRU.set(i);
                if (_NRU.count()==_size)
                    _NRU.reset();
             }
            break;
        }
        if (!remote && i<_size) {
            _L0filter.set(i);
        }
    }
    updateVictimPolicy(page,i);
}

bool
LocalPartition::write(uint32_t page,bool remote)
{
    bool isL0cached=false;
    uint32_t i=0;

    for (i=0;i<_size;i++) {
        if (_pages[i]==page) {
            if (_victimPolicy==VICTIM_NRU){
                _NRU.set(i);
                _NRUmodified.set(i);
                if (_NRU.count()==_size)
                    _NRU.reset();
            }
            isL0cached=_L0filter.test(i);
            if (remote) {
                _L0filter.set(i);
            }
            break;
        }
    }
    updateVictimPolicy(page,i);
    return isL0cached;
}

bool
LocalPartition::remove(uint32_t page)
{
  int i=0;
  for (i=0;i<_count;i++) {
      if (_pages[i]==page)
          break;
  }

  if (i!=_count) {
      _pages[i]=_pages[_count];
      _count--;
      return true;
  }
  else {
      return false;
  }

}
bool
LocalPartition::insert (uint32_t page,uint32_t &victim)
{
  if (_count<_size) {
      _pages[_count]=page;
      _count++;
      victim=0;
      insertVictimInformation(page,_count-1);
      return false;
  }
  else {
      int index = getVictimIndex(page);
      victim=_pages[index];
      _pages[index]=page;
      return true;
  }
}

void
LocalPartition::insertVictimInformation(uint32_t page,uint32_t index)
{
    switch (_victimPolicy) {
        case VICTIM_FIFO:
                         break;
        case VICTIM_NRU:
                         _NRU.set(index);
                         break;
        case VICTIM_LRU:
                         _LRU[index]=index;
                         break;
    }
}

int
LocalPartition::getVictimIndex(uint32_t page)
{
    uint32_t i=0;
    uint32_t index=0;
    int randj=0;

    switch (_victimPolicy) {
        case VICTIM_FIFO:
                         index=_victimIndex;
                         _victimIndex=(++_victimIndex)%_size;
                         break;
        case VICTIM_NRU:
                         // random on not set
                         if (_NRU.count()>0)
                             randj= (random() % _NRU.count() );
                         for (i=0;i<_size;i++) {
                             if (_NRU.test(i) && i>=randj) {
                                 index=i;
                                 break;
                             }
                         }
                         _NRU.set(index,0);
                         break;
        case VICTIM_RANDOM:
                         // use high bits
                         index =  (random() % _size );
                         break;
        case VICTIM_LRU:
                         _victimIndex=_LRU[_size-1];
                         break;
    }
    return index;
}

void
LocalPartition::resetL0Filter()
{
    _L0filter.reset();
}
